Fix hover state handling
authorMatthias Clasen <mclasen@redhat.com>
Fri, 22 Mar 2019 20:33:53 +0000 (16:33 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Fri, 22 Mar 2019 20:35:20 +0000 (16:35 -0400)
We were not paying enough attention to detail when updating
hover and focus state while generating crossing events. The
invariant that we need to preserve here is that when a widget
has focus or hover, its parent does too.

gtk/gtkmain.c

index d95890b52f9632be408dc1183f605d833ef6107f..193bd10b3ccb3e7d1e101ede763425d1be09a7c0 100644 (file)
@@ -1403,6 +1403,42 @@ get_virtual_notify_type (GdkNotifyType notify_type)
     }
 }
 
+/* Determine from crossing mode details whether the ultimate
+ * target is us or a descendant. Keep this code in sync with
+ * gtkeventcontrollerkey.c:update_focus
+ */
+static gboolean
+is_or_contains (gboolean      enter,
+                GdkNotifyType detail)
+{
+  gboolean is = FALSE;
+  gboolean contains = FALSE;
+
+  switch (detail)
+    {
+    case GDK_NOTIFY_VIRTUAL:
+    case GDK_NOTIFY_NONLINEAR_VIRTUAL:
+      is = FALSE;
+      contains = enter;
+      break;
+    case GDK_NOTIFY_ANCESTOR:
+    case GDK_NOTIFY_NONLINEAR:
+      is = enter;
+      contains = FALSE;
+      break;
+    case GDK_NOTIFY_INFERIOR:
+      is = enter;
+      contains = !enter;
+      break;
+    case GDK_NOTIFY_UNKNOWN:
+    default:
+      g_warning ("Unknown focus change detail");
+      return;
+    }
+
+  return is || contains;
+}
+
 static void
 synth_crossing (GtkWidget       *widget,
                 GtkWidget       *toplevel,
@@ -1451,7 +1487,7 @@ synth_crossing (GtkWidget       *widget,
   if (event->any.surface)
     g_object_ref (event->any.surface);
 
-  if (enter)
+  if (is_or_contains (enter, notify_type))
     gtk_widget_set_state_flags (widget, flags, FALSE);
   else
     gtk_widget_unset_state_flags (widget, flags);